#include <iostream>
#include <XnCppWrapper.h>
#include <XnVNite.h>
#include "EventDispatcher.h"

#include "../View/graphicEngine.h"
#include "../Model/AppList.h"

System * _sys;
GraphicEngine * _ge;
AppList * _list;
// HARDCODE !! ONLY FOR DEMO
XnVSelectableSlider2D* g_pTrackPad = NULL;
XnUInt32 g_TrackPadHandle = 0;


XnCallbackHandle g_nItemHoverHandle = NULL;
XnCallbackHandle g_nItemSelectHandle = NULL;
XnCallbackHandle g_nValueChangeHandle = NULL;

XnCallbackHandle g_nPrimaryCreateHandle = NULL;
XnCallbackHandle g_nPrimaryDestroyHandle = NULL;


void XN_CALLBACK_TYPE SessionStart(const XnPoint3D& pFocus, void* UserCxt) {
  _ge->activateSession();
}

void XN_CALLBACK_TYPE SessionEnd(void* UserCxt) {
  _ge->desactivateSession();
}

void XN_CALLBACK_TYPE OnSwipeLeftCB(XnFloat fVelocity, XnFloat fAngle,
    void *pUserCxt) {
  _ge->rotateLeft();
  _list->swapNext();
}

void XN_CALLBACK_TYPE OnSwipeRightCB(XnFloat fVelocity, XnFloat fAngle,
    void *pUserCxt) {
  _ge->rotateRight();
  _list->swapPrec();
}

// void XN_CALLBACK_TYPE TrackPad_ValueChange(XnFloat fXValue, XnFloat fYValue, void* cxt)
// {
//   // if(TRUE == g_isPrintValueChange)
// 	  printf("Value changed: %f, %f\n", fXValue, fYValue);
 
// }
void XN_CALLBACK_TYPE TrackPad_ItemHover(XnInt32 nXItem, XnInt32 nYItem, void* cxt)
{
    _list->getMiddleApp()->getAction()->over(nXItem,nYItem);
#ifdef DEBUG
    std::cout << "Hover: " <<  nXItem << "," << nYItem << std::endl;
#endif
}

void XN_CALLBACK_TYPE TrackPad_ItemSelect(XnInt32 nXItem, XnInt32 nYItem, XnVDirection eDir, void* cxt)
{
  _list->getMiddleApp()->getAction()->click(nXItem,nYItem);
#ifdef DEBUG
  std::cout << "Click: " <<  nXItem << "," << nYItem << std::endl;
#endif
}

void XN_CALLBACK_TYPE TrackPad_PrimaryCreate(const XnVHandPointContext* cxt, const XnPoint3D& ptFocus, void* UserCxt)
{
#ifdef DEBUG
  printf("TrackPad input has started!!!, point ID: [%d] ", cxt->nID);
  printf("Starting point position: [%f],[%f],[%f]\n", cxt->ptPosition.X, cxt->ptPosition.Y, cxt->ptPosition.Z);
#endif
}

void XN_CALLBACK_TYPE TrackPad_PrimaryDestroy(XnUInt32 nID, void* UserCxt)
{
  printf("TrackPad input has stopped!!!\n");
  //g_isInputStarted = FALSE;
}

EventDispatcher::EventDispatcher(System * sys, GraphicEngine * ge,
    AppList* list) {
  if (sys && ge && list) {
    _sys = sys;
    _ge = ge;
    _list = list;
  }

  _nRetVal = XN_STATUS_OK;

  // OpenNI Section : Start the context with XML config file for the sensor
  _nRetVal = _context.InitFromXmlFile(XML_CONFIG_FILE);
  if (_nRetVal != XN_STATUS_OK) {
    std::cerr << "Error while loading sensor config file"
        << "check if your sensor is connected" << std::endl;
  }

  // NITE section
  _sessionManager = new XnVSessionManager();
  _nRetVal = _sessionManager->Initialize(&_context, "Wave,Click", "RaiseHand");
  if (_nRetVal != XN_STATUS_OK) {
    std::cerr << "Error while starting " << std::endl;
    //exit(1);
  }

  // HARDCODE !! ONLY FOR DEMO
  XnUInt32 g_TP_XDim = 5;
  XnUInt32 g_TP_YDim = 5;
  g_pTrackPad = new XnVSelectableSlider2D(g_TP_XDim, g_TP_YDim);
  g_TrackPadHandle = _sessionManager->AddListener(g_pTrackPad);
  // Register for the Hover event of the TrackPad
  g_nItemHoverHandle = g_pTrackPad->RegisterItemHover(NULL, 
						      &TrackPad_ItemHover);
  // Register for the Value Change event of the TrackPad
  // g_nValueChangeHandle = g_pTrackPad->RegisterValueChange(NULL, 
  // 							  &TrackPad_ValueChange);
  // Register for the Select event of the TrackPad
  g_nItemSelectHandle = g_pTrackPad->RegisterItemSelect(NULL, 
							&TrackPad_ItemSelect);
  // END HARDCODE

  _sessionManager->RegisterSession(NULL, &SessionStart, &SessionEnd);

  /* Swipe Detector */
  _swipeD.RegisterSwipeLeft(NULL, OnSwipeLeftCB);
  _swipeD.RegisterSwipeRight(NULL, OnSwipeRightCB);

  _sessionManager->AddListener(&_swipeD);

}

void EventDispatcher::startObserver() {
  if (_nRetVal == XN_STATUS_OK) {
    while (true) {
      _context.WaitAndUpdateAll();
      ((XnVSessionManager*) _sessionManager)->Update(&_context);
    }
  } else {
    // if no kinect detected
    // TODO clean it
    while (true)
      ;
  }
}

EventDispatcher::~EventDispatcher() {

}

